package com.free.container;

import com.free.common.entity.ReqInfoDto;
import com.free.common.entity.ServiceInstance;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import java.util.HashSet;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

@Component
@Slf4j
public class RegisterContainer {

    /**
     * 剔除服务ip时间
     */
    private static final long DELETEIP_TIMEOUT = 1000 * 10;

    public RegisterContainer() {
        // 服务注册中心每隔3s 检查地址是否宕机
        ScheduledExecutorService scheduledService = Executors.newSingleThreadScheduledExecutor();
        scheduledService.scheduleAtFixedRate(new Heartbeat(), 3, 3, TimeUnit.SECONDS);
    }

    private static final Map<String, HashSet<ServiceInstance>> MAP_SERVICE_INSTANCES = new ConcurrentHashMap<>();

    /**
     * 服务地址 对应 ---服务最后续约时间  权重
     *
     * @param reqInfo
     * @return
     */
    public boolean put(ReqInfoDto reqInfo) {
        //1.先判断容器 是否已经注册该服务
        String serviceName = reqInfo.getServiceName();
        HashSet<ServiceInstance> serviceInstances = MAP_SERVICE_INSTANCES.get(serviceName);
        //cas syn/lock 线程池 定时心跳 续约 前端页面整合 剔除刷新 juc并发
        if (serviceInstances == null) {
            //双重检验锁
            synchronized (this) {
                serviceInstances = new HashSet<>();
                MAP_SERVICE_INSTANCES.put(serviceName, serviceInstances);
            }
        }
        // 将接口地址存放到 服务注册中心中
        ServiceInstance serviceInstance = new ServiceInstance(serviceName, reqInfo.getServiceAddress(), System.currentTimeMillis());
        return serviceInstances.add(serviceInstance);
    }

    public Map<String, HashSet<ServiceInstance>> getMapAddres() {
        return MAP_SERVICE_INSTANCES;
    }

    public ServiceInstance remove(ReqInfoDto reqInfo) {
        return findByServiceInstance(reqInfo, true);
    }

    private ServiceInstance findByServiceInstance(ReqInfoDto reqInfo, boolean isDelete) {
        String serviceAddres = reqInfo.getServiceAddress();
        if (StringUtils.isEmpty(serviceAddres)) {
            return null;
        }
        // 根据服务的名称 查找 对应 注册 多个接口地址
        HashSet<ServiceInstance> serviceInstances = MAP_SERVICE_INSTANCES.get(reqInfo.getServiceName());
        if (serviceInstances == null) {
            //没有找到服务列表
            log.error("<Service list not found>");
            return null;
        }
        // 循环遍历根据服务名称+服务地址 查找该对象
        for (ServiceInstance serviceInstance : serviceInstances) {
            if (serviceInstance.getServiceAddress().equals(serviceAddres)) {
                if (isDelete) {
                    serviceInstances.remove(serviceInstance);
                }
                return serviceInstance;
            }
        }
        return null;
    }

    private ServiceInstance findByServiceInstance(ReqInfoDto reqInfo) {
        return findByServiceInstance(reqInfo, false);
    }

    /**
     * 服务续约
     *
     * @param reqInfo
     * @return
     */
    public boolean renewal(ReqInfoDto reqInfo) {
        ServiceInstance serviceInstance = findByServiceInstance(reqInfo);
        if (serviceInstance == null) {
            // 在做服务续约时 发现 在容器中没有该服务
            return put(reqInfo);
        }
        // 最后续约时间+5000毫秒
        serviceInstance.setLastRenewalTime(serviceInstance.getLastRenewalTime() + reqInfo.getRenewalDuration());
        return true;
    }

    /**
     * 注册中心 心跳检测 线程  检查 我们注册中心上的容器 存放地址 是否宕机
     */
    static class Heartbeat implements Runnable {

        @Override
        public void run() {
            // 1.遍历容器
            MAP_SERVICE_INSTANCES.forEach((k, v) -> {
                // 2.取出每个服务 对应集群地址
                HashSet<ServiceInstance> serviceInstances = MAP_SERVICE_INSTANCES.get(k);
                serviceInstances.forEach((serviceInstance) -> {
                    // 3.系统时间-该接口地址最后续约时间>10s
                    // 续约时间  22:00:10
                    long currentTimeMillis = System.currentTimeMillis();
                    long lastRenewalTime = serviceInstance.getLastRenewalTime();
                    if (currentTimeMillis - lastRenewalTime > DELETEIP_TIMEOUT) {
                        log.info("服务名称：" + serviceInstance.getServiceName() + "," +
                                "服务地址" + serviceInstance.getServiceAddress() + ",当前系统时间:" + currentTimeMillis
                                + ",该服务上一次续约时间" + lastRenewalTime + "服务被剔除.");
                        //续约超时，直接移除 剔除该地址
                        serviceInstances.remove(serviceInstance);
                    }
                });

            });
        }
    }
}
